package indi.yudidi.classloader;

import sun.misc.URLClassPath;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;

public class MyClassLoader extends ClassLoader {
    public MyClassLoader() {
    }

    public MyClassLoader(ClassLoader parent) {
        super(parent);
    }

    /***
     * 如果不想打破双亲委派模型，那么只需要重写findClass方法即可.
     * 子类加载器去加载指定的类.
     * @param name
     * @return
     * @throws ClassNotFoundException
     */
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        File file = getClassFile(name);
        try {
            byte[] bytes = getClassBytes(file);
            Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
            return c;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return super.findClass(name);
    }

//    private File getClassFile(String name)
//    {
//        File file = new File("D:/Person.class");
//        return file;
//    }

    /**
     * YDD: 使用"线程上下文类加载器" 加载 "类路径"下的class文件
     * @param fullClassName 全类名
     * @return 返回class文件的File对象
     */
    private File getClassFile(String fullClassName) {
//        URL classpath = this.getClass().getClassLoader().getResource("");//MyClassLoader类位于类路径,是由AppClassLoader加载的.
        URL classpath = Thread.currentThread().getContextClassLoader().getResource("");//线程上下文类加载器: 默认是AppClassLoader
        fullClassName = fullClassName.replace(".", "/");
        File file = new File(classpath.getPath() + "/" + fullClassName + ".class");
        return file;
    }

    private byte[] getClassBytes(File file) throws Exception {
        // 这里要读入.class的字节，因此要使用字节流
        FileInputStream fis = new FileInputStream(file);
        FileChannel fc = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel wbc = Channels.newChannel(baos);
        ByteBuffer by = ByteBuffer.allocate(1024);

        while (true) {
            int i = fc.read(by);
            if (i == 0 || i == -1)
                break;
            by.flip();
            wbc.write(by);
            by.clear();
        }

        fis.close();

        return baos.toByteArray();
    }
}